﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Json;
using System.Xml.Linq;

namespace MailRuWrapper.Client
{
    public class ClientWrapper : BaseWrapper
    {
        public string ClientKey { get; private set; }

        public bool IsReferred
        {
            get { return RefererType.IsReferred(); }
        }

        #region InitParams

        /// <summary>
        /// Идентификатор пользователя, установившего приложение
        /// </summary>
        public String OID { get; private set; }

        /// <summary>
        /// Флаг, обозначающий установил ли приложение пользователь просматривающий приложение (если установил = 1, иначе = 0)
        /// </summary>
        public bool IsAppUser { get; private set; }

        /// <summary>
        /// Код состояния приложения, этот параметр используется для передачи состояния, в которое должно перейти приложение, если оно было вызвано не с его страницы, а например из раздела "Что нового?"
        /// </summary>
        public string State { get; private set; }

        /// <summary>
        /// Пользовательские настройки приложения. Значение данного параметра является перечисление через запятую настроек пользователя, описанных в документации к методу users.hasAppPermission. Например: ext_perm=stream,notifications
        /// </summary>
        public AppPermissionType Permissions { get; private set; }


        /// <summary>
        /// Тип реферера
        /// </summary>
        public RefererTypes RefererType { get; private set; }

        /// <summary>
        /// Идентификатор реферера
        /// </summary>
        public String RefererId { get; private set; }


        #endregion

        public ClientWrapper(String clientKey, IDictionary<String, String> initParams)
        {
            ClientKey = clientKey;
            try
            {
                AppID = Int32.Parse(initParams["app_id"]);
            }
            catch (Exception _ex)
            {
                throw new MailRuWrapperException("app_id missing", _ex);
            }

            try
            {
                SessionKey = initParams["session_key"];
            }
            catch (Exception _ex)
            {
                throw new MailRuWrapperException("session_key missing", _ex);
            }

            try
            {
                AuthenticationKey = initParams["authentication_key"];
            }
            catch (Exception _ex)
            {
                throw new MailRuWrapperException("authentication_key missing", _ex);
            }

            try
            {
                OID = initParams["oid"];
            }
            catch (Exception _ex)
            {
                throw new MailRuWrapperException("oid missing", _ex);
            }

            try
            {
                VID = initParams["vid"];
            }
            catch (Exception _ex)
            {
                throw new MailRuWrapperException("vid missing", _ex);
            }

            try
            {
                IsAppUser = false;
                if (initParams.ContainsKey("is_app_user"))
                    IsAppUser = initParams["is_app_user"] == "1";
            }
            catch (Exception _ex)
            {
                throw new MailRuWrapperException("is_app_user missing", _ex);
            }

            if (initParams.ContainsKey("state"))
                State = initParams["state"];

            try
            {
                WindowID = initParams["window_id"];
            }
            catch (Exception _ex)
            {
                throw new MailRuWrapperException("window_id missing", _ex);
            }

            RefererType = RefererTypes.None;
            try
            {
                RefererType = initParams["referer_type"].ToRefererType();
            }
            catch (Exception _ex)
            {
                //throw new MailRuWrapperException("referer_type missing", _ex);
            }

            try
            {
                RefererId = initParams["referer_id"];
            }
            catch (Exception _ex)
            {
                //throw new MailRuWrapperException("referer_id missing", _ex);
            }

            Permissions = AppPermissionType.None;
            try
            {
                if (initParams["ext_perm"] != "")
                {

                    if (initParams["ext_perm"].Contains("stream"))
                    {
                        Permissions |= AppPermissionType.Stream;
                    }
                    if (initParams["ext_perm"].Contains("notifications"))
                    {
                        Permissions |= AppPermissionType.Notifications;
                    }
                    if (initParams["ext_perm"].Contains("widget"))
                    {
                        Permissions |= AppPermissionType.Widget;
                    }
                }
            }
            catch (Exception _ex)
            {
                throw new MailRuWrapperException("ext_perm missing", _ex);
            }
        }

        #region Self

        /// <summary>
        /// Получить информацию о пользователе запустившим приложение
        /// </summary>
        /// <param name="callback"></param>
        /// <returns></returns>
        public IAsyncResult BeginGetSelfInfo(AsyncCallback callback)
        {
            return Users.BeginGetInfo(callback, uids: VID);
        }

        public IUser EndGetSelfInfo(IAsyncResult asyncResult)
        {
            return Users.EndGetInfo(asyncResult).Single();
        }

        #endregion

        internal override String GetRequestString(FuncType method, params string[] customParams)
        {
            if (AppID <= 0)
                throw new ArgumentNullException("AppID");

            var _params = new List<String>
                              {
                                  AppID.ToRequestParam("app_id"),
                                  SessionKey.ToRequestParam("session_key"),
                                  method.ToRequestParam("method"),
                                  Format.ToRequestParam("format")
                              };

            _params.AddRange(customParams.Where(item => item != ""));
            _params.Sort();


            var _uri = ApiUri + "?" +
                       _params.Aggregate((current, next) => current + "&" + next) +
                       "&sig=" + ComputeSIG(_params);

            return Uri.EscapeUriString(_uri);
        }

        private string ComputeSIG(List<string> param)
        {
            if (param == null)
                throw new ArgumentNullException("param");

            var _sBuilder = new StringBuilder();
            _sBuilder.Append(VID);
            param.Sort();
            foreach (var _item in param)
            {
                _sBuilder.Append(_item);
            }

            _sBuilder.Append(ClientKey);


            return _sBuilder.ToString().MD5Hash();
        }

        internal override object ParsResponse(string response, FuncType method, string request)
        {
            if (string.IsNullOrEmpty(response))
                throw new ArgumentNullException("response");

            switch (Format)
            {
                case ApiFormat.Json:

                    #region Json

                    try
                    {
                        var _jObject = JsonValue.Parse(response);
                        if (_jObject.ContainsKey("error"))
                        {
                            throw new MailRuWrapperRequestException(_jObject["error"]["error_code"],
                                                                    _jObject["error"]["error_msg"],
                                                                    request);
                        }
                    }
                    catch (MailRuWrapperRequestException _ex)
                    {
                        InvokeLog("Response contain error: " + _ex.Message,LogLevel.Api);
                        throw;
                    }
                    catch
                    {
                    }


                    switch (method)
                    {
                        case FuncType.FriendsGetAppUsers:
                        case FuncType.FriendsGet:
                        case FuncType.UsersGetInfo:
                            var _users = JsonValue.Parse(response);
                            return (from object _j in _users select User.Parse(_j as JsonValue)).ToArray();
                        case FuncType.UsersIsAppUser:
                            var _bool = JsonValue.Parse(response);
                            return _bool["isAppUser"].ToBoolean();
                        case FuncType.UsersHasAppPermission:
                            _bool = JsonValue.Parse(response);
                            if (_bool.ContainsKey("stream"))
                                return _bool["stream"].ToBoolean();
                            if (_bool.ContainsKey("notifications"))
                                return _bool["notifications"].ToBoolean();
                            break;
                        case FuncType.PhotosGetAlbums:
                            var _pAlbums = JsonValue.Parse(response);
                            return (from object _j in _pAlbums select PhotoAlbum.Parse(_j as JsonValue)).ToArray();
                        case FuncType.PhotosGet:
                            var _photos = JsonValue.Parse(response);
                            return (from object _j in _photos select Photo.Parse(_j as JsonValue)).ToArray();
                        case FuncType.AudiosGet:
                            var _audios = JsonValue.Parse(response);
                            return (from object _j in _audios select Audio.Parse(_j as JsonValue)).ToArray();
                        case FuncType.StreamPublish:
                            _bool = JsonValue.Parse(response);
                            return _bool["publish"].ToBoolean();
                    }
                    InvokeLog("NotImplemented " + method, LogLevel.Critical);
                    throw new NotImplementedException(method.ToString());


                    #endregion

                case ApiFormat.Xml:

                    #region Xml

                    var _xml = XDocument.Parse(response);

                    if (_xml.Root != null)
                    {
                        if (_xml.Root.Name == "error")
                        {
                            InvokeLog("Response contain error: " + _xml.Root.Element("error_msg").Value, LogLevel.Api);
                            throw new MailRuWrapperRequestException(
                                _xml.Root.Element("error_code").Value.ToInt(),
                                _xml.Root.Element("error_msg").Value,
                                    request);
                        }
                        switch (method)
                        {
                            case FuncType.FriendsGetAppUsers:
                            case FuncType.FriendsGet:
                            case FuncType.UsersGetInfo:
                                return _xml.Root.Elements().Select(User.Parse).ToArray();
                            case FuncType.UsersIsAppUser:
                            case FuncType.UsersHasAppPermission:
                            case FuncType.PaymentsOpenDialog:
                            case FuncType.StreamPublish:
                            case FuncType.AudiosLinkAudio:
                                return _xml.Root.Elements().First().Value.ToBoolean();
                            case FuncType.PhotosGetAlbums:
                                return _xml.Root.Elements().Select(PhotoAlbum.Parse).ToArray();
                            case FuncType.PhotosGet:
                                return _xml.Root.Elements().Select(Photo.Parse).ToArray();
                            case FuncType.AudiosGet:
                                return _xml.Root.Elements().Select(Audio.Parse).ToArray();
                        }
                    }
                    InvokeLog("NotImplemented " + method, LogLevel.Critical);
                    throw new NotImplementedException(method.ToString());

                    #endregion

            }

            InvokeLog("NotImplemented " + method, LogLevel.Critical);
            throw new NotImplementedException(method.ToString());
        }

    }
}
